<?php
// $Id: session.inc 28 2011-03-13 22:38:29Z mjohnson60@gmail.com $

/**
 * @file
 * User session handling functions.
 */
function sess_open($save_path, $session_name)
{
	return TRUE;
}

/**
 * Enter description here ...
 * 
 * @return boolean
 */
function sess_close()
{
	return TRUE;
}

/**
 * Enter description here ...
 * 
 * @param unknown_type $key
 * @return string
 */
function sess_read($key)
{
	global $user;
	
	// Write and Close handlers are called after destructing objects since PHP 5.0.5
	// Thus destructors can use sessions but session handler can't use objects.
	// So we are moving session closure before destructing objects.
	register_shutdown_function('session_write_close');
	
	// Handle the case of first time visitors and clients that don't store cookies (eg. web crawlers).
	if (!isset($_COOKIE[session_name()]))
	{
		$user = anonymous_user();
		return '';
	}
	
	// Otherwise, if the session is still active, we have a record of the client's session in the database.
	$user = Db::fetchObject(Db::query("SELECT u.*, s.* FROM users u INNER JOIN sessions s ON u.uid = s.uid WHERE s.sid = '%s'", $key));
	
	// We found the client's session record and they are an authenticated,
	// active user.
	if ($user && $user->uid > 0 && $user->status == 1)
	{ 
        // This is done to unserialize the data member of $user
        $user = Athena::unpack($user);

        // Add roles element to $user
        $user->roles = array();
        $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
        $result = Db::query("SELECT r.rid, r.name FROM role r INNER JOIN users_roles ur ON ur.rid = r.rid WHERE ur.uid = %d", $user->uid);
        while (($role = Db::fetchObject($result)) !== FALSE)
        {
            $user->roles[$role->rid] = $role->name;
        }
	}
	else
	{
		// We didn't find the client's record (session has expired), or they are
		// blocked, or they are an anonymous user.
		
		$session = isset($user->session) ? $user->session : '';
		$user = anonymous_user($session);
	}
	
	return $user->session;
}

/**
 * Enter description here ...
 * 
 * @param unknown_type $key
 * @param unknown_type $value
 * @return boolean
 */
function sess_write($key, $value)
{
	global $user;
	
	// If saving of session data is disabled or if the client doesn't have a session,
	// and one isn't being created ($value), do nothing. This keeps crawlers out of
	// the session table. This reduces memory and server load, and gives more useful
	// statistics. We can't eliminate anonymous session table rows without breaking
	// the throttle module and the "Who's Online" block.
	if (!session_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value)))
	{
		return TRUE;
	}
	
	Db::query("UPDATE sessions SET uid = %d, cache = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '%s'", array($user->uid, isset($user->cache) ? $user->cache : '', Athena::ipAddress(), $value, time(), $key));
	if (Db::affectedRows())
	{
		// Last access time is updated no more frequently than once every 180 seconds.
		// This reduces contention in the users table.
		if ($user->uid && time() - $user->access > Storage::get('session_write_interval', 180))
		{
			Db::query("UPDATE users SET access = %d WHERE uid = %d", array(time(), $user->uid));
		}
	}
	else
	{
		// If this query fails, another parallel request probably got here first.
		// In that case, any session data generated in this request is discarded.
		@Db::query("INSERT INTO sessions (sid, uid, cache, hostname, session, timestamp) VALUES ('%s', %d, %d, '%s', '%s', %d)", array($key, $user->uid, isset($user->cache) ? $user->cache : '', Athena::ipAddress(), $value, time()));
	}
	
	return TRUE;
}

/**
 * Called when an anonymous user becomes authenticated or vice-versa.
 */
function sess_regenerate()
{
	$old_session_id = session_id();
	
	// We code around http://bugs.php.net/bug.php?id=32802 by destroying
	// the session cookie by setting expiration in the past (a negative
	// value).  This issue only arises in PHP versions before 4.4.0,
	// regardless of the Drupal configuration.
	// TODO: remove this when we require at least PHP 4.4.0
	if (isset($_COOKIE[session_name()]))
	{
		setcookie(session_name(), '', time() - 42000, '/');
	}
	
	session_regenerate_id();
	
	Db::query("UPDATE sessions SET sid = '%s' WHERE sid = '%s'", array(session_id(), $old_session_id));
}

/**
 * Counts how many users have sessions. Can count either anonymous sessions or authenticated sessions.
 *
 * @param int $timestamp
 * A Unix timestamp representing a point of time in the past.
 * The default is 0, which counts all existing sessions.
 * @param boolean $anonymous
 * TRUE counts only anonymous users.
 * FALSE counts only authenticated users.
 * @return  int
 * The number of users with sessions.
 */
function sess_count($timestamp = 0, $anonymous = true)
{
	$query = $anonymous ? ' AND uid = 0' : ' AND uid > 0';
	return Db::result(Db::query('SELECT COUNT(sid) AS count FROM sessions WHERE timestamp >= %d' . $query, $timestamp));
}

/**
 * Called by PHP session handling with the PHP session ID to end a user's session.
 *
 * @param  string $sid
 * the session id
 */
function sess_destroy_sid($sid)
{
	Db::query("DELETE FROM sessions WHERE sid = '%s'", $sid);
}

/**
 * End a specific user's session
 *
 * @param  string $uid
 * the user id
 */
function sess_destroy_uid($uid)
{
	Db::query('DELETE FROM sessions WHERE uid = %d', $uid);
}

/**
 * Enter description here ...
 * 
 * @param unknown_type $lifetime
 * @return boolean
 */
function sess_gc($lifetime)
{
	// Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough
	// value. For example, if you want user sessions to stay in your database
	// for three weeks before deleting them, you need to set gc_maxlifetime
	// to '1814400'. At that value, only after a user doesn't log in after
	// three weeks (1814400 seconds) will his/her session be removed.
	Db::query("DELETE FROM sessions WHERE timestamp < %d", time() - $lifetime);
	
	return TRUE;
}

/**
 * Determine whether to save session data of the current request.
 *
 * This function allows the caller to temporarily disable writing of session data,
 * should the request end while performing potentially dangerous operations, such as
 * manipulating the global $user object.  See http://drupal.org/node/218104 for usage
 *
 * @param $status
 * Disables writing of session data when FALSE, (re-)enables writing when TRUE.
 * @return
 * FALSE if writing session data has been disabled. Otherwise, TRUE.
 */
function session_save_session($status = NULL)
{
	static $save_session = TRUE;
	if (isset($status))
	{
		$save_session = $status;
	}
	
	return ($save_session);
}
